//TODO ? exit -1 ou continuer traitement avec affichage erreur ?

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

#define SIZENAME 20
#define SIZEIP 16
#define BUFFERSIZE 4096

typedef struct 
	{
	char name[SIZENAME];
	char ip[SIZEIP];
	int connected;  //TODO bool_t
	}
	Contact;

typedef struct
	{
	char groupName[SIZENAME];
	int nbMembers;
	char ** members;
	}
	Group;

typedef struct 
	{
	Contact me;
	int nbContacts;
	Contact * contacts;
	int nbGroups;
	Group * groups;
	}
	AdressBook;
	
AdressBook adressBook;	

int readMe(char * user, char * buffer, int nbCharRead, int buffCursor)
{
	//TODO ajouter le test cursor > nbCharRead
	int i;
	char firstLine[7] = "Moi :\n\0";
	
	//lecture de la première ligne
	if(strncmp(firstLine, &(buffer[buffCursor]), 6) != 0)
	{
		printf("AdressBook error : expected line is \"Moi :\n\"\n");
		exit(-1);
	}
	
	buffCursor += 6;
	
	//lecture du nom d'utilisateur
	i=buffCursor;
	while(buffer[i] != ' ')
	{
		if(user[i-buffCursor] != buffer[i])
		{
			printf("AdressBook error : the user name should be your's one\n");
			exit(-1);
		}
		i++;
	}	
	buffCursor=i+1;
	
	//ajout du nom d'utilisateur
	strcpy(adressBook.me.name,user);
	
	//lecture de ": "
	if(buffer[buffCursor] != ':')
	{
		printf("AdressBook error : expected \"%s : \"\n", user);
		exit(-1);
	}
	buffCursor++;
	if(buffer[buffCursor] != ' ')
	{
		printf("AdressBook error : expected \"%s : \"\n", user);
		exit(-1);
	}
	buffCursor++;
	
	//lecture de l'adresse ip
	i=buffCursor;
	while(buffer[i] != '\n')
	{
		//TODO controler adresse ip
		adressBook.me.ip[i-buffCursor] = buffer[i];
		i++;
	}	
	buffCursor=i+1;
	
	//lecture de \n
	if(buffer[buffCursor] != '\n')
	{
		printf("AdressBook error : expected a new line after reading the block \"Moi\"\n");
		exit(-1);
	}
	buffCursor++;
	
	//pour faire joli
	adressBook.me.connected = 1;
	
	return buffCursor;
}

int readContacts(char * buffer, int nbCharRead, int buffCursor)
{
	//TODO ajouter le test cursor > nbCharRead
	int i,j, nbContacts=0;
	char firstLine[12] = "Contacts : \0";
	char c;
	char * tmp = &c;
	
	//lecture de la première ligne
	if(strncmp(firstLine, &(buffer[buffCursor]), 11) != 0)
	{
		printf("AdressBook error : expected line is \"Contacts : <nbContacts>\"\n");
		exit(-1);
	}
	
	buffCursor +=11;
	
	//lecture du nombre de contacts
	i=buffCursor;
	while(buffer[i] != '\n')
	{
		//TODO controler buffer[i] appartient à [0-9]
		c=buffer[i];
		nbContacts = nbContacts * 10 + atoi(tmp);
		i++;
	}	
	buffCursor=i+1;
	
	//allocation du nombre de contacts
	adressBook.nbContacts = nbContacts;
	adressBook.contacts = malloc (nbContacts * sizeof(Contact));
	
	//lecture de chaque contact
	for(j=0; j<nbContacts; j++)
	{
		//lecture du nom d'utilisateur
		i=buffCursor;
		while(buffer[i] != ' ')
		{
			adressBook.contacts[j].name[i-buffCursor] = buffer[i];
			i++;
		}	
		buffCursor=i+1;
			
		//lecture de ": "
		if(buffer[buffCursor] != ':')
		{
			printf("AdressBook error : expected \"%s : \"\n", adressBook.contacts[j].name);
			exit(-1);
		}
		buffCursor++;
		if(buffer[buffCursor] != ' ')
		{
			printf("AdressBook error : expected \"%s : \"\n", adressBook.contacts[j].name);
			exit(-1);
		}
		buffCursor++;
	
		//lecture de l'adresse ip
		i=buffCursor;
		while(buffer[i] != '\n')
		{
			//TODO controler adresse ip
			adressBook.contacts[j].ip[i-buffCursor] = buffer[i];
			i++;
		}	
		buffCursor=i+1;
		
		//par défaut, un contact n'est pas connecté
		adressBook.contacts[j].connected = 0;
	}
	
	//lecture de \n
	if(buffer[buffCursor] != '\n')
	{
		printf("AdressBook error : expected a new line after reading the block \"Contacts\"\n");
		exit(-1);
	}
	buffCursor++;
	
	return buffCursor;	
}

int readGroups(char * buffer, int nbCharRead, int buffCursor)
{
	//TODO ajouter le test cursor > nbCharRead
	int i,j, k, nbGroups=0, nbMembers;
	char firstLine[11] = "Groupes : \0";
	char c;
	char * tmp = &c;
	
	//lecture de la première ligne
	if(strncmp(firstLine, &(buffer[buffCursor]), 10) != 0)
	{
		printf("AdressBook error : expected line is \"Groupes : <nbGroups>\"\n");
		exit(-1);
	}
		
	buffCursor +=10;
	
	//lecture du nombre de groupes
	i=buffCursor;
	while(buffer[i] != '\n')
	{
		//TODO controler buffer[i] appartient à [0-9]
		c=buffer[i];
		nbGroups = nbGroups * 10 + atoi(tmp);
		i++;
	}	
	buffCursor=i+1;
	
	//allocation du nombre de groupes
	adressBook.nbGroups = nbGroups;
	adressBook.groups = malloc (nbGroups * sizeof(Group));
	
	//lecture de chaque contact
	for(j=0; j<nbGroups; j++)
	{
		//lecture du nom du groupe
		i=buffCursor;
		while(buffer[i] != ' ')
		{
			adressBook.groups[j].groupName[i-buffCursor] = buffer[i];
			i++;
		}	
		buffCursor=i+1;
	
		//lecture de ": "
		if(buffer[buffCursor] != ':')
		{
			printf("AdressBook error : expected \"%s : \"\n", adressBook.contacts[j].name);
			exit(-1);
		}
		buffCursor++;
		if(buffer[buffCursor] != ' ')
		{
			printf("AdressBook error : expected \"%s : \"\n", adressBook.contacts[j].name);
			exit(-1);
		}
		buffCursor++;
		
		//lecture du nombre de membres
		nbMembers = 0;
		
		i=buffCursor;
		while(buffer[i] != ' ')
		{
			//TODO controler buffer[i] appartient à [0-9]
			c=buffer[i];
			nbMembers = nbMembers * 10 + atoi(tmp);
			i++;
		}	
		buffCursor=i+1;
				
		
		//allocation des membres
		adressBook.groups[j].nbMembers = nbMembers;
		adressBook.groups[j].members = malloc(nbMembers * sizeof(char *));
		
		//TODO controller ' ' au centre, '\n' dernier, '\0' tout dernier
		for(k=0; k<nbMembers ; k++)
		{		
			adressBook.groups[j].members[k] = malloc(SIZENAME * sizeof(char));
			
			i=buffCursor;			
			while(buffer[i] != ' ' && buffer[i] != '\n' && buffer[i] != '\0')
			{
				adressBook.groups[j].members[k][i-buffCursor] = buffer[i];
				i++;
			}	
			buffCursor=i+1;
		}
	
	}
	
	return buffCursor;
}
		
void openBook(char * user)
{
	int i, nameSize, fd, buffCursor, nbCharRead;
	char * fileName;
	char buffer[BUFFERSIZE];
	
	nameSize = strlen(user); //nombre de lettres dans le nom de l'utilisateur
	fileName = malloc ((11 + nameSize) * sizeof(char)); //11 : panda_<name>.txt\0
	
	//création du nom de fichier à partir du nom d'utilisateur
	fileName[0] = 'p';
	fileName[1] = 'a';
	fileName[2] = 'n';
	fileName[3] = 'd';
	fileName[4] = 'a';
	fileName[5] = '_';
	for(i=6; i< 6+nameSize; i++)
	{
		fileName[i] = user[i-6];
	}
	fileName[i++] = '.';
	fileName[i++] = 't';
	fileName[i++] = 'x';
	fileName[i++] = 't';
	fileName[i] = '\0';
	
	//ouverture du fichier
	fd = open (fileName, O_RDONLY);
	if(fd <0)
	{
		printf("AdressBook error : could not find %s\n",fileName);
		exit(-1);
	}
	
	//première lecture du fichier
	buffCursor = 0; //curseur sur le buffer
	nbCharRead = read(fd, buffer, BUFFERSIZE);
	
	/*
	printf("aa%saa\n",buffer);
	printf("zz%czz\n",buffer[9]);
	printf("ee%see\n",&(buffer[9]));
	printf("rr%drr\n",strncmp(&(buffer[9]),&(buffer[9]),5));
	*/
	
	//lecture des infos sur le contact
	if((buffCursor=readMe(user, buffer, nbCharRead, buffCursor)) < 0)
	{
		//TODO si pb buffcursor > nbCharREad
	} 
	
	//lecture des infos sur les contacts
	if((buffCursor=readContacts(buffer, nbCharRead, buffCursor)) < 0)
	{
		//TODO si pb buffcursor > nbCharREad
	} 
	
	//lecture des infos sur les groupes
	if((buffCursor=readGroups(buffer, nbCharRead, buffCursor)) < 0)
	{
		//TODO si pb buffcursor > nbCharREad
	} 
	
	close(fd);
}

char * uitoa(int x)
{// 134 -> {'4','3','1'} -> {'1','3','4'}
	int i=0,j;
	char temp;
	char * result;
	
	result = malloc((i+1)*sizeof(char));
	result[i] = '0' + x%10;
	x/=10;
	i++;
	
	while(x>0)
	{
		result = realloc(result, (i+1)*sizeof(char));
		result[i] = '0' + x%10;
		x/=10;
		i++;
	}
	
	//inversion de result
	for(j=0;j<(i/2);j++)
	{
		temp = result[j];
		result[j] = result[i-1-j];
		result[i-1-j] = temp;
	}
	
	return result;
}

void saveBook()
{
	char firstLineMe[7] = "Moi :\n";
	char firstLineContacts[14] = "\n\nContacts : ";
	char firstLineGroups[12] = "\nGroupes : ";
	char newLine[2] = "\n";
	char newSpace[2] = " ";
	char newCombo[4] = " : ";
	char * buffer;
	char * temp;
	int buffSize=0;
	int tempSize;
	int size, sizeBis;
	int i,j;
	int nameSize, fd;
	char * fileName;
	//TODO tests : écriture dans panda_<nom>a.txt
	nameSize = strlen(adressBook.me.name); //nombre de lettres dans le nom de l'utilisateur
	//fileName = malloc (12 * nameSize * sizeof(char)); //11 : panda_<name>.txt\0
	fileName = malloc ((11 + nameSize) * sizeof(char)); //11 : panda_<name>.txt\0
	
	//création du nom de fichier à partir du nom d'utilisateur
	fileName[0] = 'p';
	fileName[1] = 'a';
	fileName[2] = 'n';
	fileName[3] = 'd';
	fileName[4] = 'a';
	fileName[5] = '_';
	for(i=6; i< 6+nameSize; i++)
	{
		fileName[i] = adressBook.me.name[i-6];
	}
	//fileName[i++] = 'a';
	fileName[i++] = '.';
	fileName[i++] = 't';
	fileName[i++] = 'x';
	fileName[i++] = 't';
	fileName[i] = '\0';
	
	//ouverture du fichier
	fd = open (fileName, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
	if(fd <0)
	{
		printf("AdressBook error : could not find %s\n",fileName);
		exit(-1);
	}
		
	//bloc Moi
	tempSize=6;
	buffer = malloc((buffSize+tempSize)*sizeof(char));
	strncpy(&(buffer[buffSize]),firstLineMe,tempSize);
	buffSize+=tempSize;
	
	tempSize = strlen(adressBook.me.name);
	buffer = realloc(buffer,(buffSize+tempSize)*sizeof(char));
	strncpy(&(buffer[buffSize]),adressBook.me.name,tempSize);
	buffSize+=tempSize;
	
	tempSize = 3;
	buffer = realloc(buffer,(buffSize+tempSize)*sizeof(char));
	strncpy(&(buffer[buffSize]),newCombo,tempSize);
	buffSize+=tempSize;
	
	tempSize = strlen(adressBook.me.ip);
	buffer = realloc(buffer,(buffSize+tempSize)*sizeof(char));
	strncpy(&(buffer[buffSize]),adressBook.me.ip,tempSize);
	buffSize+=tempSize;
	
	//bloc Contacts
	tempSize=13;
	buffer = realloc(buffer,(buffSize+tempSize)*sizeof(char));
	strncpy(&(buffer[buffSize]),firstLineContacts,tempSize);
	buffSize+=tempSize;
	
	size = adressBook.nbContacts;
	temp = uitoa(size);
	tempSize = strlen(temp);
	buffer = realloc(buffer,(buffSize+tempSize)*sizeof(char));
	strncpy(&(buffer[buffSize]),temp,tempSize);
	buffSize+=tempSize;
	free(temp);
	
	tempSize = 1;
	buffer = realloc(buffer,(buffSize+tempSize)*sizeof(char));
	strncpy(&(buffer[buffSize]),newLine,tempSize);
	buffSize+=tempSize;
	
	for(i=0; i<size; i++)
	{
		tempSize = strlen(adressBook.contacts[i].name);
		buffer = realloc(buffer,(buffSize+tempSize)*sizeof(char));
		strncpy(&(buffer[buffSize]),adressBook.contacts[i].name,tempSize);
		buffSize+=tempSize;
	
		tempSize = 3;
		buffer = realloc(buffer,(buffSize+tempSize)*sizeof(char));
		strncpy(&(buffer[buffSize]),newCombo,tempSize);
		buffSize+=tempSize;
	
		tempSize = strlen(adressBook.contacts[i].ip);
		buffer = realloc(buffer,(buffSize+tempSize)*sizeof(char));
		strncpy(&(buffer[buffSize]),adressBook.contacts[i].ip,tempSize);
		buffSize+=tempSize;
		
		tempSize = 1;
		buffer = realloc(buffer,(buffSize+tempSize)*sizeof(char));
		strncpy(&(buffer[buffSize]),newLine,tempSize);
		buffSize+=tempSize;
	}
	
	//bloc groupes
	tempSize=11;
	buffer = realloc(buffer,(buffSize+tempSize)*sizeof(char));
	strncpy(&(buffer[buffSize]),firstLineGroups,tempSize);
	buffSize+=tempSize;
	
	size = adressBook.nbGroups;
	temp = uitoa(size);
	tempSize = strlen(temp);
	buffer = realloc(buffer,(buffSize+tempSize)*sizeof(char));
	strncpy(&(buffer[buffSize]),temp,tempSize);
	buffSize+=tempSize;
	free(temp);
	
	tempSize = 1;
	buffer = realloc(buffer,(buffSize+tempSize)*sizeof(char));
	strncpy(&(buffer[buffSize]),newLine,tempSize);
	buffSize+=tempSize;
	
	for(i=0; i<size; i++)
	{
		tempSize = strlen(adressBook.groups[i].groupName);
		buffer = realloc(buffer,(buffSize+tempSize)*sizeof(char));
		strncpy(&(buffer[buffSize]),adressBook.groups[i].groupName,tempSize);
		buffSize+=tempSize;
	
		tempSize = 3;
		buffer = realloc(buffer,(buffSize+tempSize)*sizeof(char));
		strncpy(&(buffer[buffSize]),newCombo,tempSize);
		buffSize+=tempSize;
		
		sizeBis = adressBook.groups[i].nbMembers;
		temp = uitoa(sizeBis);
		tempSize = strlen(temp);
		buffer = realloc(buffer,(buffSize+tempSize)*sizeof(char));
		strncpy(&(buffer[buffSize]),temp,tempSize);
		buffSize+=tempSize;
		free(temp);
		
		tempSize = 1;
		buffer = realloc(buffer,(buffSize+tempSize)*sizeof(char));
		strncpy(&(buffer[buffSize]),newSpace,tempSize);
		buffSize+=tempSize;
		
		for(j=0; j<sizeBis-1; j++)
		{
			tempSize = strlen(adressBook.groups[i].members[j]);
			buffer = realloc(buffer,(buffSize+tempSize)*sizeof(char));
			strncpy(&(buffer[buffSize]),adressBook.groups[i].members[j],tempSize);
			buffSize+=tempSize;
			
			tempSize = 1;
			buffer = realloc(buffer,(buffSize+tempSize)*sizeof(char));
			strncpy(&(buffer[buffSize]),newSpace,tempSize);
			buffSize+=tempSize;
		}
		
		tempSize = strlen(adressBook.groups[i].members[j]);
		buffer = realloc(buffer,(buffSize+tempSize)*sizeof(char));
		strncpy(&(buffer[buffSize]),adressBook.groups[i].members[j],tempSize);
		buffSize+=tempSize;
		
		tempSize = 1;
		buffer = realloc(buffer,(buffSize+tempSize)*sizeof(char));
		strncpy(&(buffer[buffSize]),newLine,tempSize);
		buffSize+=tempSize;
	}
	
	buffer = realloc(buffer,(buffSize)*sizeof(char));
	buffer[buffSize]= '\0';
	
	printf("%s\n",buffer);//TODO enlever ça
	
	write(fd, buffer, buffSize);
	
	close(fd);
}

void freeBook()
{
	int i,j;
	
	memset(adressBook.me.name, '\0', SIZENAME);
	memset(adressBook.me.ip, '\0', SIZENAME);
	adressBook.me.connected = 0;
	
	for(i=0; i<adressBook.nbContacts; i++)
	{
		memset(adressBook.contacts[i].name, '\0', SIZENAME);
		memset(adressBook.contacts[i].ip, '\0', SIZENAME);
		adressBook.contacts[i].connected = 0;
	}
	free(adressBook.contacts);
	adressBook.nbContacts = 0;
	
	for(i=0; i<adressBook.nbGroups; i++)
	{
		for(j=0; j<adressBook.groups[i].nbMembers; j++)
		{
			free(adressBook.groups[i].members[j]);
		}
		adressBook.groups[i].nbMembers = 0;
		free(adressBook.groups[i].members);
	}
	free(adressBook.groups);
	adressBook.nbGroups = 0;
}

void printBook()
{
	int i,j;
	printf("Moi :\n");
	printf("%s : %s\n", adressBook.me.name, adressBook.me.ip);
	printf("\n");
	
	printf("Contacts : %d\n",adressBook.nbContacts);
	for(i=0;i<adressBook.nbContacts;i++)
	{
		printf("%s : %s\n", adressBook.contacts[i].name, adressBook.contacts[i].ip);
	}
	printf("\n");
	
	printf("Groupes : %d\n",adressBook.nbGroups);
	for(i=0;i<adressBook.nbGroups;i++)
	{
		printf("%s : %d", adressBook.groups[i].groupName, adressBook.groups[i].nbMembers);
		for(j=0;j<adressBook.groups[i].nbMembers;j++)
		{
			printf(" %s", adressBook.groups[0].members[j]);
		}
		printf("\n");
	}
	printf("\n");	
}

void addContact(char * name, char * ip)
{
	int index;

	if(strlen(name)>SIZENAME)
	{
		printf("Add Contact error : name %s is too long\n", name);
		exit(-1);
	}
	
	if(strlen(ip)>SIZEIP)
	{
		printf("Add Contact error : ip %s is too long\n", ip);
		exit(-1);
	}
	
	if(searchContactIndex(name) == -1)
	{
		adressBook.nbContacts++;
	
		adressBook.contacts = realloc(adressBook.contacts, adressBook.nbContacts * sizeof(Contact));
	
		strcpy(adressBook.contacts[adressBook.nbContacts-1].name,name);
		strcpy(adressBook.contacts[adressBook.nbContacts-1].ip,ip);
		adressBook.contacts[adressBook.nbContacts-1].connected = 1;
	
		saveBook();
	}
	else
	{
		printf("Add Contact error : contact %s already exist\n",name);
		exit(-1);
	}	
}

int searchContactIndex(char * name)
{
	int find=0, i=0; //TODO find -> bool
	
	while(!find && i<adressBook.nbContacts)
	{
		if(strcmp(adressBook.contacts[i].name, name) != 0)
		{
			i++;
		}
		else
		{
			find = 1;
		}
	}
	
	if(find)
	{
		return i;
	}
	else
	{
		return -1;
	}	
}

//TODO int -> bool
//1 -> co / 0 -> deco
int coDecoContact(char * name, int state)
{
	int i;
	i=searchContactIndex(name);
	
	if(i!=-1)
	{
		if(state)
		{
			adressBook.contacts[i].connected = 1;
		}
		else
		{
			adressBook.contacts[i].connected = 0;
		}
	}
	
	return i;
}

void connectionContact(char * name)
{
	int i;
	
	if(strlen(name)>SIZENAME)
	{
		printf("Connection Contact error : name %s is too long\n", name);
		exit(-1);
	}
	
	i = coDecoContact(name, 1);
	
	if(i==-1)
	{
		printf("Connection Contact error : contact %s not found\n", name);
		exit(-1);
	}
}

void deconnectionContact(char * name)
{
	int i;
	
	if(strlen(name)>SIZENAME)
	{
		printf("Deconnection Contact error : name %s is too long\n", name);
		exit(-1);
	}
	
	i = coDecoContact(name, 0);
	
	if(i==-1)
	{
		printf("Deconnection Contact error : contact %s not found\n", name);
		exit(-1);
	}
}

int main(int argc, char** argv)
{
	char laurane[8] = "laurane";
	char lauraneIp[15] = "142.125.200.10";

	if(argc != 2)
	{
		printf("Usage : %s <userName>\n",argv[0]);
		exit(-1);
	}
	
	if(strlen(argv[1])>SIZENAME)
	{
		printf("Parameter error : user name too long\n");
		exit(-1);
	}
	
	printf("\n####\nappel de open book\n####\n\n");
	openBook(argv[1]);
	
	printf("\n####\nappel de print book : affichage du carnet d'adresse (structure)\n####\n\n");
	printBook();
	
	printf("\n####\nappel de add contact : ajout du contact laurane\n\t appel a save book : affichage du buffer d'écriture\n####\n\n");
	addContact(laurane,lauraneIp);
	
	//printf("\n####\nappel de print book : affichage du carnet d'adresse (structure)\n####\n\n");
	//printBook();
	
	//printf("\n####\nappel de connection contact : connection du contact laurane\n####\n\n");
	//connectionContact(laurane);
	
	printf("\n####\nappel de free book\n####\n\n");
	freeBook();

	return 0;
}
